#include "TcpServer.h"
#include <cstring>

static EventLoop *checkLoopNotNull(EventLoop *loop)
{
    if (loop == nullptr)
    {
        LOG_FATAL("tcpserver loop is null");
    }
    return loop;
}

TcpServer::TcpServer(EventLoop *loop, const InetAddress &listenAddr, const std::string &name, Option option)
    : loop_(checkLoopNotNull(loop)), ipPort_(listenAddr.toIpPort()), name_(name), acceptor_(new Acceptor(loop, listenAddr, option == Option::KReusePort)), threadPool_(new EvenLoopThreadPool(loop, name)), connectionCallback_(), messageCallback_()
    , nextConnId_(0), started_(0)
    
{
    // 其实这里将分发函数设置为新链接的回调
    // 实现有新链接后执行分发
    acceptor_->setNewConnectionCallback(std::bind(
        &TcpServer::newConnection,
        this,
        std::placeholders::_1,
        std::placeholders::_2));
}

TcpServer::~TcpServer()
{
    for (auto &item : connections_)
    {
        // 交给智能指针自动析构
        TcpConnectionPtr conn(item.second);
        item.second.reset();

        conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
    }
}

void TcpServer::setThreadNum(int numThreads) // 设置subloop数量
{
    threadPool_->setThreadNum(numThreads);
}

void TcpServer::start() // 开启监听
{
    if (started_++ == 0)
    {
        threadPool_->start(threadInitCallback_);
        loop_->runInLoop(std::bind(
            &Acceptor::listen,
            acceptor_.get()));
    }
}

static InetAddress getLocalAddrByFd(int sockfd)
{
    sockaddr_in local;
    socklen_t addrlen = sizeof local;
    std::memset(&local, 0, sizeof local);
    if (::getsockname(sockfd, (sockaddr *)&local, &addrlen) < 0)
    {
        LOG_ERROR("get local addr error");
    }
    InetAddress localAddr(local);
    return localAddr;
}

// 分发线程
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
    EventLoop *workLoop = threadPool_->getNextLoop();
    const std::string connName = name_ + ipPort_ + "#" + std::to_string(nextConnId_++);
    LOG_INFO("TcpServer::newConnection [%s] - connection name : [%s] from %s", name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());

    // 通过sockfd获取本机的ip地址和端口信息
    InetAddress localAddr = getLocalAddrByFd(sockfd);

    TcpConnectionPtr conn(new TcpConnection(workLoop, connName, sockfd, localAddr, peerAddr));
    conn->setConnectionCallback(connectionCallback_);
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);

    conn->setCloseCallback(std::bind(&TcpConnection::connectDestroyed, conn));

        // 2. 必须调用 connectEstablished，推荐用 runInLoop 保证在正确的 loop 线程中
    workLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));

}
void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{
    loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr &conn)
{
    LOG_INFO("removeConnectionInLoop start name:%s,  conn name: %s", name_.c_str(), conn->name().c_str());
    connections_.erase(conn->name());
    EventLoop *loop = conn->getLoop();
    loop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
